#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
通用网站爬虫工具 - 使用 Claude API 从任意网站 URL 提取通讯作者信息
支持命令行直接使用：python crawler_others.py <URL>
"""

import argparse
import json
import logging
import sys
import traceback
from typing import Dict, List, Optional
import anthropic
import httpx

from crawl_base import ArticleCrawler
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException, WebDriverException
import time

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.StreamHandler()
    ]
)
logger = logging.getLogger(__name__)


class OtherSitesCrawler(ArticleCrawler):
    """
    通用网站爬虫类，使用 Claude API 从任意网站提取通讯作者信息
    """
    
    def __init__(self, claude_api_key: str = None):
        """
        初始化通用网站爬虫
        
        Args:
            claude_api_key: Claude API 密钥
        """
        # 调用父类构造函数，获得反爬虫能力
        super().__init__()
        
        self.claude_api_key = claude_api_key or "sk-P2DK3uQ9gEHV5zvP8PqnoTZvkGvNn4EbNkg2k4YNnBnOalg7"
        self.claude_client = None
        
        # 初始化Claude API客户端
        self._init_claude_client()

    def _init_claude_client(self):
        """初始化Claude API客户端"""
        try:
            # 首先尝试使用代理
            proxy_url = "http://127.0.0.1:7890"
            
            try:
                # 创建一个配置了代理和禁用SSL验证的httpx客户端
                http_client_with_proxy_and_no_ssl = httpx.Client(
                    transport=httpx.HTTPTransport(
                        proxy=proxy_url,
                        retries=3  # 增加重试次数
                    ),
                    verify=False,
                    timeout=httpx.Timeout(60.0, connect=10.0),  # 增加超时时间
                    limits=httpx.Limits(max_connections=10, max_keepalive_connections=5)
                )
                
                self.claude_client = anthropic.Anthropic(
                    api_key=self.claude_api_key,
                    http_client=http_client_with_proxy_and_no_ssl
                )
                logger.info("Claude API 客户端初始化成功（使用代理）")
                
            except Exception as proxy_error:
                logger.warning(f"使用代理初始化Claude客户端失败: {proxy_error}")
                logger.info("尝试无代理模式...")
                
                # 如果代理失败，尝试直连
                http_client_no_proxy = httpx.Client(
                    verify=False,
                    timeout=httpx.Timeout(60.0, connect=10.0),  # 增加超时时间
                    limits=httpx.Limits(max_connections=10, max_keepalive_connections=5),
                    transport=httpx.HTTPTransport(retries=3)  # 增加重试次数
                )
                
                self.claude_client = anthropic.Anthropic(
                    api_key=self.claude_api_key,
                    http_client=http_client_no_proxy
                )
                logger.info("Claude API 客户端初始化成功（无代理模式）")
                
        except Exception as e:
            logger.error(f"Claude API 客户端初始化完全失败: {e}")
            self.claude_client = None

    def _is_valid_corresponding_author(self, name: str, email: str) -> bool:
        """
        验证是否为有效的通讯作者信息，排除编辑团队等非个人作者
        
        Args:
            name: 作者姓名
            email: 作者邮箱
            
        Returns:
            bool: 是否为有效的通讯作者
        """
        if not name or not email:
            return False
            
        name_lower = name.lower().strip()
        email_lower = email.lower().strip()
        
        # 排除的关键词列表
        excluded_keywords = [
            'team', 'office', 'support', 'service', 'editorial', 'admin',
            'editor', 'board', 'staff', 'department', 'desk', 'center',
            'centre', 'group', 'committee', 'panel', 'division', 'unit',
            'bureau', 'agency', 'organization', 'organisation', 'company',
            'corporation', 'institute', 'institution', 'foundation',
            'society', 'association', 'council', 'commission'
        ]
        
        # 检查姓名是否包含排除的关键词
        for keyword in excluded_keywords:
            if keyword in name_lower:
                logger.debug(f"排除作者 '{name}': 姓名包含关键词 '{keyword}'")
                return False
        
        # 排除的邮箱前缀
        excluded_email_prefixes = [
            'info@', 'support@', 'editor@', 'editorial@', 'office@',
            'admin@', 'contact@', 'general@', 'help@', 'service@',
            'team@', 'staff@', 'desk@', 'center@', 'centre@'
        ]
        
        # 检查邮箱前缀
        for prefix in excluded_email_prefixes:
            if email_lower.startswith(prefix):
                logger.debug(f"排除作者 '{name}': 邮箱前缀为 '{prefix}'")
                return False
        
        # 检查姓名是否看起来像个人姓名（至少包含两个单词，通常是名和姓）
        name_words = name.strip().split()
        if len(name_words) < 2:
            # 如果只有一个单词，检查是否明显不是个人姓名
            single_word = name_words[0].lower()
            if any(keyword in single_word for keyword in excluded_keywords):
                logger.debug(f"排除作者 '{name}': 单词姓名包含排除关键词")
                return False
        
        # 检查是否为明显的非个人姓名模式
        non_personal_patterns = [
            'advanced team', 'editorial team', 'journal team', 'publishing team',
            'technical support', 'customer service', 'help desk', 'editorial board',
            'editorial office', 'journal office', 'publishing office'
        ]
        
        for pattern in non_personal_patterns:
            if pattern in name_lower:
                logger.debug(f"排除作者 '{name}': 匹配非个人姓名模式 '{pattern}'")
                return False
        
        logger.debug(f"验证通过: '{name}' <{email}>")
        return True


    def fetch_html_content(self, url: str) -> Optional[str]:
        """
        获取网页HTML内容，使用父类的反爬虫策略
        
        Args:
            url: 网页URL
            
        Returns:
            HTML内容字符串，失败时返回None
        """
        try:
            logger.info(f"正在使用反爬虫策略获取网页内容: {url}")
            # 使用父类的反爬虫方法获取WebDriver实例
            driver = self.fetch_article_html_enhanced(url)
            if driver:
                try:
                    # 获取页面源码
                    html_content = driver.page_source
                    logger.info(f"成功获取网页内容，长度: {len(html_content)} 字符")
                    return html_content
                finally:
                    # 确保关闭WebDriver
                    driver.quit()
            else:
                logger.error(f"无法获取网页内容: {url}")
                return None
        except Exception as e:
            logger.error(f"获取网页内容失败: {e}")
            return None

    def fetch_webdriver_instance(self, url: str):
        """
        获取WebDriver实例，用于交互式操作
        
        Args:
            url: 网页URL
            
        Returns:
            WebDriver实例，失败时返回None
        """
        try:
            logger.info(f"正在获取WebDriver实例: {url}")
            # 使用父类的反爬虫方法获取WebDriver实例
            driver = self.fetch_article_html_enhanced(url)
            if driver:
                logger.info(f"成功获取WebDriver实例: {url}")
                return driver
            else:
                logger.error(f"无法获取WebDriver实例: {url}")
                return None
        except Exception as e:
            logger.error(f"获取WebDriver实例失败: {e}")
            return None

    def extract_authors_from_html_snippet(self, html_snippet: str, url: str) -> List[Dict]:
        """
        使用Claude API从局部HTML片段中提取通讯作者信息（专门处理交互后的局部内容）
        
        Args:
            html_snippet: 局部HTML片段内容
            url: 文章URL
            
        Returns:
            通讯作者信息列表 [{"name": "姓名", "email": "邮箱"}, ...]
        """
        if not self.claude_client:
            logger.warning("Claude API客户端未初始化")
            return []
        
        try:
            prompt = f"""
请从以下HTML片段中提取通讯作者（Corresponding Author）的姓名和邮箱地址。

这是一个局部HTML片段，通常包含作者信息和邮件链接。请特别注意：

**识别通讯作者的标识：**
1. CSS类名包含 "corr-email", "corresponding", "author-email" 等
2. 文本中包含 "Corresponding Author", "E-mail:", "Contact:" 等标识
3. href="mailto:" 链接，特别是在作者信息区域的邮箱链接
4. 邮箱地址可能以各种形式出现，如 "author@domain.com" 或 "author [at] domain.com"

**提取规则：**
1. 优先提取明确标注为通讯作者的邮箱
2. 如果没有明确标注，但邮箱在作者信息区域，也可以提取
3. 作者姓名可能在邮箱附近，如果找不到姓名，可以从邮箱地址推断（如 john.smith@university.edu -> John Smith）
4. 如果无法确定姓名，可以使用 "Unknown Author" 作为占位符

**严格排除以下内容（这些不是通讯作者）：**
1. 编辑团队：如 "Editorial Team", "Advanced Team", "Editorial Board", "advancedteam@"
2. 期刊团队：如 "Journal Team", "Publishing Team", "Editorial Office"
3. 技术支持：如 "Technical Support", "Customer Service", "Help Desk"
4. 通用邮箱：如 "info@", "support@", "editor@", "editorial@", "office@"
5. 机构邮箱：如 "admin@", "contact@", "general@"
6. 任何包含 "team", "office", "support", "service", "editorial", "admin" 等词汇的邮箱或姓名

**输出格式：**
- 返回JSON格式的数组，每个元素包含name和email字段
- 如果找到通讯作者邮箱但无法确定姓名，使用从邮箱推断的姓名或 "Unknown Author"
- 如果没有找到符合条件的通讯作者，返回空数组[]

HTML片段：
{html_snippet}

请返回JSON格式的结果：
"""
            
            message = self.claude_client.messages.create(
                model="claude-3-5-haiku-20241022",
                max_tokens=1000,
                messages=[
                    {"role": "user", "content": prompt}
                ]
            )
            
            response_text = message.content[0].text.strip()
            
            # 尝试解析JSON
            try:
                # 提取JSON部分
                if '```json' in response_text:
                    json_start = response_text.find('```json') + 7
                    json_end = response_text.find('```', json_start)
                    json_text = response_text[json_start:json_end].strip()
                elif '[' in response_text and ']' in response_text:
                    json_start = response_text.find('[')
                    json_end = response_text.rfind(']') + 1
                    json_text = response_text[json_start:json_end]
                else:
                    json_text = response_text
                
                authors = json.loads(json_text)
                
                # 验证数据格式
                if isinstance(authors, list):
                    valid_authors = []
                    for author in authors:
                        if isinstance(author, dict) and 'name' in author and 'email' in author:
                            # 标准化邮箱格式
                            email = author['email']
                            if isinstance(email, list):
                                # 如果邮箱是列表，取第一个
                                email = email[0] if email else ""
                            elif not isinstance(email, str):
                                # 如果不是字符串也不是列表，转换为字符串
                                email = str(email)
                            
                            email = email.replace(' [at] ', '@').replace(' at ', '@')
                            name = author['name'].strip()
                            email = email.strip()
                            
                            # 使用验证函数过滤非个人作者
                            if self._is_valid_corresponding_author(name, email):
                                valid_authors.append({
                                    'name': name,
                                    'email': email
                                })
                            else:
                                logger.info(f"过滤掉非个人作者: {name} <{email}>")
                    
                    logger.info(f"从HTML片段成功提取 {len(valid_authors)} 位通讯作者信息")
                    return valid_authors
                else:
                    logger.warning("Claude API返回的不是数组格式")
                    return []
                    
            except json.JSONDecodeError as e:
                logger.error(f"解析Claude API返回的JSON失败: {e}")
                logger.debug(f"原始响应: {response_text}")
                return []
                
        except anthropic.PermissionDeniedError as e:
            logger.warning(f"Claude API权限不足，跳过AI处理: {e}")
            logger.info("提示：请联系API提供商检查用户组权限")
            logger.info("直接返回NULL（空列表），不进行HTML分析")
            return []
        except anthropic.AuthenticationError as e:
            logger.error(f"Claude API认证失败: {e}")
            logger.info("提示：请检查API密钥是否正确")
            logger.info("直接返回NULL（空列表），不进行HTML分析")
            return []
        except anthropic.RateLimitError as e:
            logger.warning(f"Claude API请求频率限制: {e}")
            logger.info("提示：请稍后重试或检查API配额")
            logger.info("直接返回NULL（空列表），不进行HTML分析")
            return []
        except Exception as e:
            logger.error(f"调用Claude API失败: {e}")
            logger.info("直接返回NULL（空列表），不进行HTML分析")
            return []

    def _extract_relevant_content(self, html_content: str) -> str:
        """
        从完整HTML中提取包含作者信息的相关部分
        
        Args:
            html_content: 完整的HTML内容
            
        Returns:
            包含作者信息的HTML片段
        """
        # 移除大量的CSS和JavaScript
        import re
        
        # 移除style标签和其内容
        html_content = re.sub(r'<style[^>]*>.*?</style>', '', html_content, flags=re.DOTALL | re.IGNORECASE)
        
        # 移除script标签和其内容
        html_content = re.sub(r'<script[^>]*>.*?</script>', '', html_content, flags=re.DOTALL | re.IGNORECASE)
        
        # 查找包含作者信息的关键部分
        author_patterns = [
            r'<[^>]*(?:author|corresponding|contact)[^>]*>.*?</[^>]*>',
            r'<[^>]*class="[^"]*(?:author|corresponding|contact)[^"]*"[^>]*>.*?</[^>]*>',
            r'<[^>]*id="[^"]*(?:author|corresponding|contact)[^"]*"[^>]*>.*?</[^>]*>',
            r'.*?(?:corresponding|通讯).*?(?:author|作者).*?',
            r'.*?(?:author|作者).*?(?:corresponding|通讯).*?',
            r'.*?@.*?\..*?',  # 邮箱模式
        ]
        
        relevant_sections = []
        
        # 尝试找到包含作者信息的部分
        for pattern in author_patterns:
            matches = re.findall(pattern, html_content, flags=re.DOTALL | re.IGNORECASE)
            relevant_sections.extend(matches)
        
        # 如果找到相关部分，返回这些部分
        if relevant_sections:
            combined_content = '\n'.join(relevant_sections)
            # 限制长度以避免超过API限制
            if len(combined_content) > 20000:
                combined_content = combined_content[:20000]
            return combined_content
        
        # 如果没有找到特定的作者部分，返回HTML的主要内容部分
        # 尝试提取body内容
        body_match = re.search(r'<body[^>]*>(.*?)</body>', html_content, flags=re.DOTALL | re.IGNORECASE)
        if body_match:
            body_content = body_match.group(1)
            # 限制长度
            if len(body_content) > 30000:
                body_content = body_content[:30000]
            return body_content
        
        # 最后的备选方案：返回前30000字符
        return html_content[:30000]

    def extract_authors_from_html(self, html_content: str, url: str) -> List[Dict]:
        """
        使用Claude API从完整HTML内容中提取通讯作者信息
        
        Args:
            html_content: 页面HTML内容
            url: 文章URL
            
        Returns:
            通讯作者信息列表 [{"name": "姓名", "email": "邮箱"}, ...]
        """
        if not self.claude_client:
            logger.warning("Claude API客户端未初始化")
            return []
        
        try:
            # 记录HTML内容的基本信息
            logger.info(f"HTML内容长度: {len(html_content)} 字符")
            logger.info(f"HTML内容前500字符: {html_content[:500]}")
            
            # 检查HTML中是否包含作者相关关键词
            author_keywords = ['author', 'corresponding', 'email', '@', 'contact', 'affiliation']
            found_keywords = [kw for kw in author_keywords if kw.lower() in html_content.lower()]
            logger.info(f"HTML中发现的作者相关关键词: {found_keywords}")
            
            # 智能提取相关内容部分
            relevant_content = self._extract_relevant_content(html_content)
            logger.info(f"提取的相关内容长度: {len(relevant_content)} 字符")
            logger.info(f"相关内容前1000字符: {relevant_content[:1000]}")
            
            prompt = f"""
请从以下HTML内容中提取所有通讯作者（Corresponding Author）的姓名和邮箱地址。

**必须提取的内容：**
1. 只提取明确标注为通讯作者的真实个人信息
2. 邮箱地址可能以各种形式出现，如 "author@domain.com" 或 "author [at] domain.com"
3. 请将所有邮箱地址转换为标准格式（使用@符号）

**严格排除以下内容（这些不是通讯作者）：**
1. 编辑团队：如 "Editorial Team", "Advanced Team", "Editorial Board"
2. 期刊团队：如 "Journal Team", "Publishing Team", "Editorial Office"
3. 技术支持：如 "Technical Support", "Customer Service", "Help Desk"
4. 通用邮箱：如 "info@", "support@", "editor@", "editorial@", "office@"
5. 机构邮箱：如 "admin@", "contact@", "general@"
6. 任何包含 "team", "office", "support", "service", "editorial", "admin" 等词汇的姓名
7. 非个人姓名：如公司名、部门名、职位名

**验证规则：**
1. 姓名必须是真实的个人姓名（通常包含名和姓）
2. 邮箱应该对应个人而非机构通用邮箱
3. 如果姓名看起来像团队、部门或职位名称，则排除
4. 如果邮箱是通用的机构邮箱，则排除

**输出格式：**
- 返回JSON格式的数组，每个元素包含name和email字段
- 只包含确认的个人通讯作者信息
- 如果没有找到符合条件的个人通讯作者，返回空数组[]

HTML内容：
{relevant_content}

请返回JSON格式的结果：
"""
            
            logger.info(f"发送给Claude的prompt长度: {len(prompt)} 字符")
            logger.debug(f"Claude prompt: {prompt[:1000]}...")
            
            message = self.claude_client.messages.create(
                model="claude-3-5-sonnet-20241022",
                max_tokens=1000,
                messages=[
                    {"role": "user", "content": prompt}
                ]
            )
            
            response_text = message.content[0].text.strip()
            logger.info(f"Claude API响应长度: {len(response_text)} 字符")
            logger.info(f"Claude API完整响应: {response_text}")
            
            # 尝试解析JSON
            try:
                # 提取JSON部分
                if '```json' in response_text:
                    json_start = response_text.find('```json') + 7
                    json_end = response_text.find('```', json_start)
                    json_text = response_text[json_start:json_end].strip()
                    logger.info(f"从markdown代码块中提取JSON: {json_text}")
                elif '[' in response_text and ']' in response_text:
                    json_start = response_text.find('[')
                    json_end = response_text.rfind(']') + 1
                    json_text = response_text[json_start:json_end]
                    logger.info(f"从响应中提取JSON数组: {json_text}")
                else:
                    json_text = response_text
                    logger.info(f"直接使用响应作为JSON: {json_text}")
                
                logger.info(f"准备解析的JSON文本: {json_text}")
                authors = json.loads(json_text)
                logger.info(f"JSON解析成功，类型: {type(authors)}, 内容: {authors}")
                
                # 验证数据格式
                if isinstance(authors, list):
                    valid_authors = []
                    for i, author in enumerate(authors):
                        logger.info(f"处理第{i+1}个作者: {author}")
                        if isinstance(author, dict) and 'name' in author and 'email' in author:
                            # 标准化邮箱格式
                            email = author['email'].replace(' [at] ', '@').replace(' at ', '@')
                            name = author['name'].strip()
                            email = email.strip()
                            
                            # 使用验证函数过滤非个人作者
                            if self._is_valid_corresponding_author(name, email):
                                valid_author = {
                                    'name': name,
                                    'email': email
                                }
                                valid_authors.append(valid_author)
                                logger.info(f"有效作者: {valid_author}")
                            else:
                                logger.info(f"过滤掉非个人作者: {name} <{email}>")
                        else:
                            logger.warning(f"无效的作者数据格式: {author}")
                    
                    logger.info(f"成功提取 {len(valid_authors)} 位通讯作者信息")
                    if valid_authors:
                        logger.info(f"提取的作者列表: {valid_authors}")
                    return valid_authors
                else:
                    logger.warning(f"Claude API返回的不是数组格式，而是: {type(authors)}")
                    logger.warning(f"返回内容: {authors}")
                    return []
                    
            except json.JSONDecodeError as e:
                logger.error(f"解析Claude API返回的JSON失败: {e}")
                logger.error(f"尝试解析的JSON文本: {json_text}")
                logger.error(f"原始响应: {response_text}")
                return []
                
        except anthropic.PermissionDeniedError as e:
            logger.warning(f"Claude API权限不足，跳过AI处理: {e}")
            logger.info("提示：请联系API提供商检查用户组权限")
            return []
        except anthropic.AuthenticationError as e:
            logger.error(f"Claude API认证失败: {e}")
            logger.info("提示：请检查API密钥是否正确")
            return []
        except anthropic.RateLimitError as e:
            logger.warning(f"Claude API请求频率限制: {e}")
            logger.info("提示：请稍后重试或检查API配额")
            return []
        except Exception as e:
            logger.error(f"调用Claude API失败: {e}")
            return []

    def extract_authors_with_interactive_method(self, driver, url: str) -> List[Dict]:
        """
        使用交互式方法提取作者信息：点击邮件图标并提取局部HTML
        
        Args:
            driver: WebDriver实例
            url: 文章URL
            
        Returns:
            通讯作者信息列表 [{"name": "姓名", "email": "邮箱"}, ...]
        """
        try:
            logger.info("开始使用交互式方法提取作者信息...")
            
            # 查找所有邮件图标
            email_icons = driver.find_elements(By.CSS_SELECTOR, 'a[href^="mailto:"]')
            logger.info(f"找到 {len(email_icons)} 个邮件图标")

            if not email_icons:
                logger.warning("在页面上未找到邮件图标，尝试其他选择器...")
                # 尝试其他可能的邮件图标选择器
                alternative_selectors = [
                    'a[href*="mailto"]',
                    '.email-icon',
                    '.author-email',
                    '[data-email]',
                    'i.fa-envelope',
                    '.envelope-icon'
                ]
                
                for selector in alternative_selectors:
                    email_icons = driver.find_elements(By.CSS_SELECTOR, selector)
                    if email_icons:
                        logger.info(f"使用选择器 '{selector}' 找到 {len(email_icons)} 个邮件图标")
                        break

            if not email_icons:
                logger.warning("未找到任何邮件图标，直接返回NULL（空列表），不进行HTML分析")
                return []

            all_authors_info = []

            # 逐个处理每个邮件图标
            for i, icon in enumerate(email_icons):
                try:
                    logger.info(f"正在处理第 {i+1} 个邮件图标...")
                    
                    # 使用JavaScript点击以显示邮件
                    driver.execute_script("arguments[0].click();", icon)
                    time.sleep(0.5)  # 等待UI更新

                    # 定位包含作者信息的最小父级容器
                    author_container = icon.find_element(By.XPATH, "./ancestor::div[1]")
                    
                    # 获取这个容器的HTML内容
                    author_html_snippet = author_container.get_attribute('outerHTML')
                    logger.debug(f"提取到的局部HTML片段: {author_html_snippet}")

                    # 将这个精确的HTML片段发送给AI进行分析
                    extracted_info = self.extract_authors_from_html_snippet(author_html_snippet, url)
                    
                    if extracted_info:
                        logger.info(f"从局部HTML中成功提取到信息: {extracted_info}")
                        all_authors_info.extend(extracted_info)
                    else:
                        logger.warning("AI未能从该局部HTML片段中提取信息。")

                except Exception as e:
                    logger.error(f"处理第 {i+1} 个邮件图标时出错: {e}")

            # 汇总并去重
            unique_authors_dict = {}
            for author in all_authors_info:
                email = author.get('email')
                if email and email not in unique_authors_dict:
                    # 只添加尚未记录的邮箱，确保保留第一次提取到的最准确的信息
                    unique_authors_dict[email] = author

            unique_authors = list(unique_authors_dict.values())
            
            if unique_authors:
                logger.info(f"交互式方法最终提取到 {len(unique_authors)} 位唯一的通讯作者信息")
            else:
                logger.warning("交互式方法未能提取到任何通讯作者信息")
                logger.info("直接返回NULL（空列表），不进行HTML分析")
            
            return unique_authors

        except Exception as e:
            logger.error(f"交互式提取作者信息时发生错误: {e}")
            logger.info("直接返回NULL（空列表），不进行HTML分析")
            return []

    def extract_title_from_url(self, url: str) -> str:
        """
        从给定URL提取文章标题
        
        Args:
            url: 文章URL
            
        Returns:
            文章标题字符串
        """
        try:
            logger.info(f"开始从URL提取文章标题: {url}")
            
            # 尝试获取WebDriver实例来获取页面标题
            driver = self.fetch_webdriver_instance(url)
            if driver:
                try:
                    # 等待页面加载完成
                    WebDriverWait(driver, 10).until(
                        lambda d: d.execute_script("return document.readyState") == "complete"
                    )
                    
                    # 获取页面标题
                    title = driver.title
                    if title and title.strip():
                        logger.info(f"成功提取页面标题: {title}")
                        return title.strip()
                    else:
                        logger.warning("页面标题为空，尝试从URL提取")
                        
                except Exception as e:
                    logger.warning(f"从WebDriver获取标题失败: {e}")
            
            # 如果WebDriver方法失败，从URL路径提取标题作为备选方案
            from urllib.parse import urlparse
            parsed_url = urlparse(url)
            path_parts = parsed_url.path.strip('/').split('/')
            if path_parts and path_parts[-1]:
                title = path_parts[-1][:50]  # 限制长度
                logger.info(f"从URL路径提取标题: {title}")
                return title
            else:
                logger.warning("无法从URL提取标题，使用默认值")
                return "未知标题"
                
        except Exception as e:
            logger.error(f"提取标题时发生错误: {e}")
            return "提取失败"

    def extract_authors_from_url(self, url: str) -> List[Dict]:
        """
        从给定URL提取通讯作者信息
        
        Args:
            url: 文章URL
            
        Returns:
            通讯作者信息列表 [{"name": "姓名", "email": "邮箱"}, ...]
        """
        try:
            logger.info(f"开始从URL提取通讯作者信息: {url}")
            
            # 首先尝试获取WebDriver实例进行交互式提取
            try:
                driver = self.fetch_webdriver_instance(url)
                if driver:
                    logger.info("成功获取WebDriver实例，尝试交互式方法...")
                    authors = self.extract_authors_with_interactive_method(driver, url)
                    
                    # 如果交互式方法成功提取到作者信息，直接返回
                    if authors:
                        logger.info(f"交互式方法成功提取到 {len(authors)} 位通讯作者信息")
                        return authors
                    else:
                        logger.warning("交互式方法未能提取到作者信息")
                        logger.info("直接返回NULL（空列表），不进行HTML分析")
                        return []
                else:
                    logger.warning("无法获取WebDriver实例")
                    logger.info("直接返回NULL（空列表），不进行HTML分析")
                    return []
                    
            except Exception as e:
                logger.warning(f"交互式方法失败: {e}")
                logger.info("直接返回NULL（空列表），不进行HTML分析")
                return []
            
        except Exception as e:
            logger.error(f"从URL提取作者信息时发生错误: {e}")
            return []

    def is_claude_available(self) -> bool:
        """检查Claude API是否可用"""
        return self.claude_client is not None


def main():
    """主函数 - 命令行接口"""
    # 默认测试URL
    default_url = "https://pubs.acs.org/doi/full/10.1021/acsanm.5c00427"
    
    parser = argparse.ArgumentParser(
        description='从任意网站URL提取通讯作者信息',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog='''
使用示例:
  python crawler_others.py  # 使用默认URL
  python crawler_others.py https://example.com/article
  python crawler_others.py https://example.com/article --api-key your_claude_api_key
  python crawler_others.py --output json  # 使用默认URL，JSON格式输出
        '''
    )
    
    parser.add_argument('url', nargs='?', default=default_url, 
                       help=f'要分析的文章URL (默认: {default_url})')
    parser.add_argument('--api-key', help='Claude API密钥（可选）')
    parser.add_argument('--output', choices=['json', 'text'], default='text', 
                       help='输出格式 (默认: text)')
    parser.add_argument('--verbose', '-v', action='store_true', 
                       help='显示详细日志')
    
    args = parser.parse_args()
    
    # 设置日志级别
    if args.verbose:
        logging.getLogger().setLevel(logging.DEBUG)
    
    # 初始化爬虫
    crawler = OtherSitesCrawler(claude_api_key=args.api_key)
    
    if not crawler.is_claude_available():
        logger.error("Claude API 不可用，请检查API密钥和网络连接")
        sys.exit(1)
    
    # 提取作者信息
    logger.info(f"开始分析URL: {args.url}")
    authors = crawler.extract_authors_from_url(args.url)
    
    # 输出结果
    if args.output == 'json':
        print(json.dumps(authors, ensure_ascii=False, indent=2))
    else:
        # 文本格式输出
        if not authors:
            print("未找到通讯作者信息")
        else:
            print(f"\n找到 {len(authors)} 位通讯作者:")
            print("=" * 50)
            for i, author in enumerate(authors, 1):
                print(f"{i}. 姓名: {author.get('name', 'N/A')}")
                print(f"   邮箱: {author.get('email', 'N/A')}")
                if author.get('affiliation'):
                    print(f"   机构: {author.get('affiliation')}")
                print()


if __name__ == "__main__":
    main()